/**
 * @file st7796.c
 * @author cangyu (sky.kirto@qq.com)
 * @brief
 * @version 0.1
 * @date 2024-09-05
 *
 * @copyright Copyright (c) 2024, CorAL. All rights reserved.
 *
 */

/* ==================== [Includes] ========================================== */

#include "../lcd_drive_internal.h"

/* ==================== [Defines] =========================================== */

#define ST7796_RESOLUTION_HOR 320
#define ST7796_RESOLUTION_VER 480

#define TAG "st7796"

/* ==================== [Typedefs] ========================================== */

typedef struct _st7796_cmd_t {
    uint8_t cmd;
    uint8_t data[14];
    uint32_t len;
} st7796_cmd_t;

/* ==================== [Static Prototypes] ================================= */

static xf_err_t st7796_init(lcd_t self);
static xf_err_t st7796_draw_bitmap(lcd_t self, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *bitmap);
static xf_err_t st7796_set_window(lcd_t self, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);

/* ==================== [Static Variables] ================================== */

const static lcd_drive_out_t st7796_out = {
    .res_hor = ST7796_RESOLUTION_HOR,
    .res_ver = ST7796_RESOLUTION_VER,
    .init = st7796_init,
    .draw_bitmap = st7796_draw_bitmap
};

/* ==================== [Macros] ============================================ */

/* ==================== [Global Functions] ================================== */

void lcd_st7796_vfunc_register(void)
{
    lcd_controller_driver_reg(SCR_CONTROLLER_st7796, &st7796_out);
}

/* ==================== [Static Functions] ================================== */

static xf_err_t st7796_init(lcd_t self)
{
    const static st7796_cmd_t init[] = {
        {0xf0, {0xc3}, 1},
        {0xf0, {0x96}, 1},
        {0x36, {0x28}, 1},
        {0x3a, {0x55}, 1},
        {0xb4, {0x01}, 1},
        {0xb7, {0xc6}, 1},
        {0xe8, {0x40, 0x8a, 0x00, 0x00, 0x29, 0x19, 0xa5, 0x33}, 8},
        {0xc1, {0x06}, 1},
        {0xc2, {0xa7}, 1},
        {0xc5, {0x18}, 1},
        {0xe0, {0xf0, 0x09, 0x0b, 0x06, 0x04, 0x15, 0x2f, 0x54, 0x42, 0x3c, 0x17, 0x14, 0x18, 0x1b}, 14},
        {0xe1, {0xf0, 0x09, 0x0b, 0x06, 0x04, 0x03, 0x2d, 0x43, 0x42, 0x3b, 0x16, 0x14, 0x17, 0x1b}, 14},
        {0xf0, {0x3c}, 1},
        {0xf0, {0x69}, 1},
        {0x29, {0x69}, 1},
    };
    xf_err_t ret = XF_OK;

    XF_LOGI(TAG, "st7796 init");

    ret |= LCD_RESET(self);
    for (int i = 0; i < sizeof(init) / sizeof(init[0]); i++) {
        ret |= LCD_WRITE_CMD(self, &init[i].cmd, 1);
        if (init[i].len != 0) {
            ret |= LCD_WRITE_DATA(self, init[i].data, init[i].len);
        }
    }
    ret |= LCD_BACKLIGHT(self, 1);

    XF_LOGI(TAG, "st7796 init ret:%d", ret);

    return ret;
}

static xf_err_t st7796_draw_bitmap(lcd_t self, uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint8_t *bitmap)
{
    xf_err_t ret = XF_OK;

    ret |= st7796_set_window(self, x, y, x + w - 1, y + h - 1);
    uint32_t len = w * h;
    XF_LOGI(TAG, "st7796_draw_bitmap len:%d", len);
    ret |= LCD_WRITE_DATA(self, bitmap, len * 2);
    XF_LOGI(TAG, "st7796_draw_bitmap");
    return 0;
}


static xf_err_t st7796_set_window(lcd_t self, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
    xf_err_t err = XF_OK;
    lcd_drive_offset(self, &x0, &y0, &x1, &y1);
    static st7796_cmd_t init[] = {
        {0x2A, {0}, 4},
        {0x2B, {0}, 4},
        {0x2C, {0}, 0},
    };
    init[0].data[0] = (x0 >> 8) & 0xff;
    init[0].data[1] = x0 & 0xff;
    init[0].data[2] = (x1 >> 8) & 0xff;
    init[0].data[3] = x1 & 0xff;
    init[1].data[0] = (y0 >> 8) & 0xff;
    init[1].data[1] = y0 & 0xff;
    init[1].data[2] = (y1 >> 8) & 0xff;
    init[1].data[3] = y1 & 0xff;

    for (int i = 0; i < sizeof(init) / sizeof(init[0]); i++) {
        err |= LCD_WRITE_CMD(self, &init[i].cmd, 1);
        if (init[i].len != 0) {
            err |= LCD_WRITE_DATA(self, init[i].data, init[i].len);
        }
    }

    return err;
}
